﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace D.UtilCore.Utils
{
    public static class ExpressionUtil
    {
        public static Expression<Func<T, bool>> True<T>() { return f => true; }
        public static Expression<Func<T, bool>> False<T>() { return f => false; }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                                      Expression<Func<T, bool>> expr2)
        {
            if (expr1 != null && expr2 == null)
            {
                return expr1;
            }

            if (expr1 == null && expr2 != null)
            {
                return expr2;
            }
            
            var parameter = Expression.Parameter(typeof(T));

            var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
            var left = leftVisitor.Visit(expr1.Body);

            var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
            var right = rightVisitor.Visit(expr2.Body);

            return Expression.Lambda<Func<T, bool>>(Expression.Or(left, right), parameter);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
                                                       Expression<Func<T, bool>> expr2)
        {
            if (expr1 != null && expr2 == null)
            {
                return expr1;
            }

            if (expr1 == null && expr2 != null)
            {
                return expr2;
            }

            var parameter = Expression.Parameter(typeof(T));

            var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
            var left = leftVisitor.Visit(expr1.Body);

            var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
            var right = rightVisitor.Visit(expr2.Body);

            return Expression.Lambda<Func<T, bool>>(Expression.And(left, right), parameter);
        }

        /// <summary>
        /// 字段名转换  Expression<Func<T, object>>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static Expression<Func<T, object>> GetOrderObjectExpression<T>(string propertyName,string sortOrder, ref bool orderByAsc)
        {
            if (string.IsNullOrWhiteSpace(propertyName)) return null;
            if(sortOrder?.ToUpper()=="DESC")//降序
                orderByAsc=false;
            var parameter = Expression.Parameter(typeof(T), "x");
            var propertyInfo = typeof(T).GetProperty(propertyName);
            if (propertyInfo == null)
                return null;

            var propertyAccess = Expression.MakeMemberAccess(parameter, propertyInfo);
            var cast = Expression.Convert(propertyAccess, typeof(object));
            var expression = Expression.Lambda<Func<T, object>>(cast, parameter);
            return expression;
        }

        private class ReplaceExpressionVisitor : ExpressionVisitor
        {
            private readonly Expression _oldValue;
            private readonly Expression _newValue;

            public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
            {
                _oldValue = oldValue;
                _newValue = newValue;
            }

            public override Expression Visit(Expression node)
            {
                if (node == _oldValue)
                {
                    return _newValue;
                }
                return base.Visit(node);
            }
        }
    }
}
